#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pymongo import MongoClient, errors
from collections import OrderedDict
from math import sqrt
import logging
import sys

logging.basicConfig(level=logging.INFO)

MONGO_HOST = 'localhost'
MONGO_PORT = 27017


def dot_product(v1, v2):
    return sum(a * b for a, b in zip(v1, v2))


def magnitude(vector):
    return sqrt(dot_product(vector, vector))


def simi_cos(v1, v2):
    '''calculate cosine similarity
    '''
    return dot_product(v1, v2) / (magnitude(v1) * magnitude(v2) + .00000000001)


def simv_sort(v1, v2):
    ''' v1 = [(u1, r1), (u2, r2), ...]
             v2 = [(u1, r1), (u3, r3), ...]
    '''
    vector = sorted([x[0] for x in v1] + [x[0] for x in v2])
    vector = set(vector)
    v1 = dict(v1)
    v2 = dict(v2)
    vec1 = [float(x) for x in v1.values()]
    vec2 = [float(x) for x in v2.values()]
    for u in vector:
        if u not in v1:
            vec1.append(0)
        elif u not in v2:
            vec2.append(0)
    simv = simi_cos(vec1, vec2)
    return round(simv, 3)


def load_movies(inputfile):
    with open(inputfile) as f:
        for row in f:
            yield row.strip().split(",")[1]


def recommend_product(movies, db):
    collect_adj = db.adjacence
    collect_movie = db.movie
    for x in movies:
        recommend = dict()
        rs = collect_movie.find_one({"_id": x})
        if not rs:
            # logging.warning(
            #     "cannot find movie: [{}] - records: {}".format(x, rs))
            continue
        v1 = rs["users"]
        adj_movies = collect_adj.find_one({"_id": x})['adjacence']
        # movie have no adjacent, skipped
        if not adj_movies:
            continue
        for k in adj_movies:
            rows = collect_movie.find_one({"_id": k})
            if not rows:
                # logging.warning(
                #     "cannot find movie: [{}] - records: {}".format(k, rows))
                continue
            v2 = rows["users"]
            simv = simv_sort(v1, v2)
            recommend[k] = simv
        recommend = OrderedDict(
            sorted(recommend.items(), key=lambda t: t[1], reverse=True))
        recommend = [(k, v) for k, v in recommend.items()]
        record = {'_id': x, 'recommend': recommend}
        yield record


def bulks(collection, docs):
    for doc in docs:
        if doc:
            try:
                collection.insert(doc)
                logging.info("inserted successful...")
            except errors.DuplicateKeyError:
                pass


def print_usage():
    usage = '''
Usage:
    python simi_computing  [inputfile]
        '''
    print(usage)


if __name__ == '__main__':
    opts = sys.argv[1:]
    if len(opts) != 1:
        print_usage()
        sys.exit(-1)

    inf = opts[0]
    print('connect to database...\n')
    client = MongoClient(MONGO_HOST, MONGO_PORT)
    db = client.movieRecommend
    collection = db.recommend

    print('loading dataset...\n')
    movies = load_movies(inf)
    docs = recommend_product(movies, db)

    print('start uploading...\n')
    bulks(collection, docs)

    print(':)  complete!')
